﻿using System;
using System.Collections.Generic;
using IrrlichtLime;
using IrrlichtLime.Core;
using Timed;
using IrrlichtLime.Scene;
using IrrlichtLime.Video;
using System.Globalization;

namespace Iso
{
    public class Scene : IScene
    {
        public Color faderColor;
        public Texture foreground;

        public string name;
        public MeshSceneNode basePlane, forePlane;
        public Texture back;
        public Rectf camLimits;
        public Vector3Df startPos;
        public float spriteRotation;

        public FieldPlayer player;
        public Dictionary<string, AnimatedSprite> objects;
        public List<PositionTrigger> triggers;
        public Dictionary<string, FieldNPC> npcs;
        public List<Vector2Df> collisions;
        public List<Vector2Df> collisionsInverse;
        public TriangleSelector sel;

        public Dictionary<string, Menu> menus;
        public Dictionary<string, UIText> texts;
        public Dictionary<string, Recti> rects;
        public Dictionary<string, UISprite> sprites;
        public Dictionary<string, ICustomPlugin> plugins;
        public MousePointer pointer;

        public Vector3Df originalCamPos, originalCamTarget;

        // DEBUG
        bool debugInfo;
        int editMode = 0; // 0:OFF 1:COLLISIONS 2:COLLISIONSINVERSE  3:SPRITEMOVE
        int currentSprite = 0;
        public List<string> spritesKeys;

        public Scene(string name)
        {
            this.name = name;
            faderColor = new Color(0, 0, 0, 0);
            camLimits = new Rectf(-5000, -5000, 5000, 5000);
            spriteRotation = 0f;

            menus = new Dictionary<string, Menu>();
            texts = new Dictionary<string, UIText>();
            rects = new Dictionary<string, Recti>();
            sprites = new Dictionary<string, UISprite>();
            plugins = new Dictionary<string, ICustomPlugin>();
            collisions = new List<Vector2Df>();
            collisionsInverse = new List<Vector2Df>();
        }

        public Scene(string name, Vector3Df position)
        {
            this.name = name;
            faderColor = new Color(0, 0, 0, 0);
            camLimits = new Rectf(-5000, -5000, 5000, 5000);
            startPos = position;
            spriteRotation = 0f;

            menus = new Dictionary<string, Menu>();
            texts = new Dictionary<string, UIText>();
            rects = new Dictionary<string, Recti>();
            sprites = new Dictionary<string, UISprite>();
            plugins = new Dictionary<string, ICustomPlugin>();
            collisions = new List<Vector2Df>();
            collisionsInverse = new List<Vector2Df>();
        }

        public virtual void Load()
        {
            // Camera
            if (Engine.camera != null)
            {
                Engine.camera.RemoveAnimators();
                Engine.camera.RemoveChildren();
                Engine.camera.Remove();
            }
            Engine.camera = Engine.device.SceneManager.AddCameraSceneNode();

            // BasePlane
            basePlane = Engine.device.SceneManager.AddMeshSceneNode(Engine.device.SceneManager.AddHillPlaneMesh("baseplane", new Dimension2Df(100f, 100f), new Dimension2Di(1, 1)));
            basePlane.SetMaterialFlag(MaterialFlag.Lighting, false);
            basePlane.SetMaterialType(MaterialType.TransparentAlphaChannel);
            basePlane.SetMaterialFlag(MaterialFlag.BilinearFilter, false);
            if (System.IO.File.Exists("data\\field\\" + name + "\\0.png"))
                basePlane.SetMaterialTexture(0, Engine.device.VideoDriver.GetTexture("data/field/" + name + "/0.png"));
            else if (System.IO.File.Exists("data\\field\\" + name + "\\0.jpg"))
                basePlane.SetMaterialTexture(0, Engine.device.VideoDriver.GetTexture("data/field/" + name + "/0.jpg"));
            else
                basePlane.Visible = false;
            sel = Engine.device.SceneManager.CreateTriangleSelector(basePlane.Mesh, basePlane);
            if (System.IO.File.Exists("data\\field\\" + name + "\\back.jpg"))
                back = Engine.device.VideoDriver.GetTexture("data/field/" + name + "/back.jpg");
            if (System.IO.File.Exists("data\\field\\" + name + "\\back.png"))
                back = Engine.device.VideoDriver.GetTexture("data/field/" + name + "/back.png");

            // ForePlane
            forePlane = Engine.device.SceneManager.AddMeshSceneNode(Engine.device.SceneManager.AddHillPlaneMesh("foreplane", new Dimension2Df(100f, 100f), new Dimension2Di(1, 1)));
            forePlane.Position = new Vector3Df(0, 31, -20.5f);
            forePlane.SetMaterialFlag(MaterialFlag.Lighting, false);
            forePlane.SetMaterialFlag(MaterialFlag.BilinearFilter, false);
            forePlane.SetMaterialType(MaterialType.TransparentAlphaChannel);
            if (System.IO.File.Exists("data\\field\\" + name + "\\1.png"))
                forePlane.SetMaterialTexture(0, Engine.device.VideoDriver.GetTexture("data/field/" + name + "/1.png"));
            else
                forePlane.Visible = false;

            // Objects
            objects = new Dictionary<string, AnimatedSprite>();
            spritesKeys = new List<string>();
            
            // Collisions
            if (System.IO.File.Exists("data/field/" + name + "/collisions.txt"))
            {
                string[] lines = ("data/field/" + name + "/collisions.txt").GetFileLines();
                foreach (string line in lines)
                {
                    string[] v = line.Split(';');
                    Vector2Df point = new Vector2Df(float.Parse(v[0], CultureInfo.InvariantCulture), float.Parse(v[1], CultureInfo.InvariantCulture));
                    collisions.Add(point);
                }
                lines = ("data/field/" + name + "/collisionsInverse.txt").GetFileLines();
                foreach (string line in lines)
                {
                    string[] v = line.Split(';');
                    Vector2Df point = new Vector2Df(float.Parse(v[0], CultureInfo.InvariantCulture), float.Parse(v[1], CultureInfo.InvariantCulture));
                    collisionsInverse.Add(point);
                }
            }

            // Triggers
            triggers = new List<PositionTrigger>();

            // NPCs
            npcs = new Dictionary<string, FieldNPC>();

            // Lua Init
            Engine.lua.NewTable(name);
            Engine.lua.DoFile("data/field/" + name + "/scripts.lua");

            Engine.device.OnEvent += device_OnEvent;
        }

        private bool device_OnEvent(Event evnt)
        {
            if (evnt.Type == EventType.Key)
            {
                LuaInterface.LuaFunction f = Engine.lua.GetFunction(name + ".KeyEvent");
                if (f != null)
                    f.Call(evnt.Key.Key);
            }
            else if (evnt.Type == EventType.Mouse)
            {
                LuaInterface.LuaFunction f = Engine.lua.GetFunction(name + ".MouseEvent");
                if (f != null)
                    f.Call(evnt.Mouse.Type);
            }
            foreach (ICustomPlugin plugin in plugins.Values)
                plugin.OnEvent(evnt);

            return false;
        }

        public virtual void Unload()
        {
            if (pointer != null)
                pointer.Delete();
            foreach (Menu menu in menus.Values)
                menu.Delete();
            foreach (UIText text in texts.Values)
                text.Delete();
            foreach (UISprite sprite in sprites.Values)
                sprite.Delete();

            Engine.device.OnEvent -= device_OnEvent;

            basePlane.Remove();
            forePlane.Remove();
            if (player != null)
            {
                player.Remove();
                player = null; 
            }
            foreach (AnimatedSprite sprite in objects.Values)
                sprite.Remove();
            foreach (FieldNPC npc in npcs.Values)
                npc.Remove();
            foreach (ICustomPlugin plugin in plugins.Values)
                plugin.Unload();
            // TODO: Unload all lua data for scene
            //Engine.lua.DoString("for k in pairs (" + name + ") do\n" + name + " [k] = nil\nend");
        }

        public virtual void Update()
        {
            if (player != null)
                player.Update();
            foreach (AnimatedSprite sprite in objects.Values)
                sprite.Update();
            for (int i = 0; i < triggers.Count; i++)
                if (triggers[i].Check(player.sprite.sceneNode.Position) == 2)
                    triggers.RemoveAt(i);
            foreach (FieldNPC npc in npcs.Values)
                npc.Update(player == null ? null : player.sprite.sceneNode.Position);

            if (player != null)
            {
                Vector3Df posClamped = new Vector3Df(player.sprite.sceneNode.Position.X.Clamp(camLimits.UpperLeftCorner.X, camLimits.LowerRightCorner.X),
                    player.sprite.sceneNode.Position.Y,
                    player.sprite.sceneNode.Position.Z.Clamp(camLimits.UpperLeftCorner.Y, camLimits.LowerRightCorner.Y));
                Engine.camera.Position = posClamped + originalCamPos;
                Engine.camera.Target = posClamped + originalCamTarget;
            }

            LuaInterface.LuaFunction f = Engine.lua.GetFunction(name + ".Update");
            if (f != null)
                f.Call();
            foreach (ICustomPlugin plugin in plugins.Values)
                plugin.OnUpdate();

            #region DEBUG
            if (Keyboard.IsKeyDown(KeyCode.F9) && debugInfo)
            {
                if (editMode == 1)
                    editMode = 0;
                else
                    editMode = 1;
                Keyboard.Reset();
            }
            if (Keyboard.IsKeyDown(KeyCode.F10) && debugInfo)
            {
                if (editMode == 2)
                    editMode = 0;
                else
                    editMode = 2;
                Keyboard.Reset();
            }
            if (Keyboard.IsKeyDown(KeyCode.F11) && debugInfo)
            {
                if (editMode == 3)
                    editMode = 0;
                else
                    editMode = 3;
                Keyboard.Reset();
            }
            if (Keyboard.IsKeyDown(KeyCode.F12))
            {
                debugInfo = !debugInfo;
                Keyboard.Reset();
            }
            if (debugInfo && editMode == 0) // NO EDIT
            {
                Vector3Df p;
                Triangle3Df t;
                SceneNode n;
                if (Engine.device.SceneManager.SceneCollisionManager.GetCollisionPoint
                    (Engine.device.SceneManager.SceneCollisionManager.GetRayFromScreenCoordinates(Engine.device.CursorControl.Position), sel, out p, out t, out n))
                    Engine.device.SetWindowCaption("FPS: " + Engine.device.VideoDriver.FPS + " SELECT: " + p.ToString() + " POS: " + player.sprite.sceneNode.Position.ToString());
                else
                    Engine.device.SetWindowCaption("FPS: " + Engine.device.VideoDriver.FPS + " POS: " + player.sprite.sceneNode.Position.ToString());
                player.colActive = true;
            }
            else if (debugInfo && editMode == 1) // COLLISIONS
            {
                Engine.device.SetWindowCaption("COLLISIONS ADD MODE");
                player.colActive = false;

                if (Keyboard.IsKeyDown(KeyCode.Return))
                {
                    Engine.device.SetWindowCaption("COLLISIONS ADD MODE - SELECT: " + player.sprite.sceneNode.Position.ToString());
                    collisions.Add(new Vector2Df(player.sprite.sceneNode.Position.X, player.sprite.sceneNode.Position.Z));
                    SceneNode sn = Engine.device.SceneManager.AddCubeSceneNode(0.5f, null, -1, player.sprite.sceneNode.Position);
                    sn.SetMaterialFlag(MaterialFlag.Lighting, false);
                    if (System.IO.File.ReadAllText("data\\field\\" + name + "\\collisions.txt") == "")
                        System.IO.File.AppendAllText("data\\field\\" + name + "\\collisions.txt",
                            player.sprite.sceneNode.Position.X.ToString(CultureInfo.InvariantCulture) + ";" + player.sprite.sceneNode.Position.Z.ToString(CultureInfo.InvariantCulture));
                    else
                        System.IO.File.AppendAllText("data\\field\\" + name + "\\collisions.txt",
                            "\n" + player.sprite.sceneNode.Position.X.ToString(CultureInfo.InvariantCulture) + ";" + player.sprite.sceneNode.Position.Z.ToString(CultureInfo.InvariantCulture));
                    Keyboard.Reset();
                }
            }
            else if (debugInfo && editMode == 2) // COLLISIONSINVERSE
            {
                Engine.device.SetWindowCaption("COLLISIONS INVERSE ADD MODE");
                player.colActive = false;

                if (Keyboard.IsKeyDown(KeyCode.Return))
                {
                    Engine.device.SetWindowCaption("COLLISIONS INVERSE ADD MODE - SELECT: " + player.sprite.sceneNode.Position.ToString());
                    collisionsInverse.Add(new Vector2Df(player.sprite.sceneNode.Position.X, player.sprite.sceneNode.Position.Z));
                    SceneNode sn = Engine.device.SceneManager.AddCubeSceneNode(0.5f, null, -1, player.sprite.sceneNode.Position);
                    sn.SetMaterialFlag(MaterialFlag.Lighting, false);
                    if (System.IO.File.ReadAllText("data\\field\\" + name + "\\collisionsInverse.txt") == "")
                        System.IO.File.AppendAllText("data\\field\\" + name + "\\collisionsInverse.txt",
                            player.sprite.sceneNode.Position.X.ToString(CultureInfo.InvariantCulture) + ";" + player.sprite.sceneNode.Position.Z.ToString(CultureInfo.InvariantCulture));
                    else
                        System.IO.File.AppendAllText("data\\field\\" + name + "\\collisionsInverse.txt",
                            "\n" + player.sprite.sceneNode.Position.X.ToString(CultureInfo.InvariantCulture) + ";" + player.sprite.sceneNode.Position.Z.ToString(CultureInfo.InvariantCulture));
                    Keyboard.Reset();
                }
            }
            else if (debugInfo && editMode == 3) // SPRITEMOVE
            {
                if (objects.Count == 0)
                    Engine.device.SetWindowCaption("SPRITE MOVE: ERR. NO SPRITES");
                else
                {
                    Engine.device.SetWindowCaption("SPRITE MOVE: " + spritesKeys[currentSprite] + " POS: " + objects[spritesKeys[currentSprite]].sceneNode.Position.ToString());
                    player.colActive = false;

                    if (Keyboard.IsKeyDown(KeyCode.PageUp))
                    {
                        currentSprite = (currentSprite + 1).Clamp(0, spritesKeys.Count - 1);
                        Keyboard.Reset();
                    }
                    else if (Keyboard.IsKeyDown(KeyCode.PageDown))
                    {
                        currentSprite = (currentSprite - 1).Clamp(0, spritesKeys.Count - 1);
                        Keyboard.Reset();
                    }
                    else if (Keyboard.IsKeyDown(KeyCode.Home))
                    {
                        objects[spritesKeys[currentSprite]].sceneNode.Position += new Vector3Df(1, 0, 0);
                        Keyboard.Reset();
                    }
                    else if (Keyboard.IsKeyDown(KeyCode.End))
                    {
                        objects[spritesKeys[currentSprite]].sceneNode.Position += new Vector3Df(-1, 0, 0);
                        Keyboard.Reset();
                    }
                    else if (Keyboard.IsKeyDown(KeyCode.Insert))
                    {
                        objects[spritesKeys[currentSprite]].sceneNode.Position += new Vector3Df(0, 0, 1);
                        Keyboard.Reset();
                    }
                    else if (Keyboard.IsKeyDown(KeyCode.Delete))
                    {
                        objects[spritesKeys[currentSprite]].sceneNode.Position += new Vector3Df(0, 0, -1);
                        Keyboard.Reset();
                    }
                    else if (Keyboard.IsKeyDown(KeyCode.KeyQ))
                    {
                        objects[spritesKeys[currentSprite]].sceneNode.Position += new Vector3Df(0, 1, 0);
                        Keyboard.Reset();
                    }
                    else if (Keyboard.IsKeyDown(KeyCode.Tab))
                    {
                        objects[spritesKeys[currentSprite]].sceneNode.Position += new Vector3Df(0, -1, 0);
                        Keyboard.Reset();
                    }
                }
            }
            else if (player != null)
                player.colActive = true;
            #endregion
        }

        public virtual void Draw()
        {
            Engine.device.VideoDriver.BeginScene();
            foreach (ICustomPlugin plugin in plugins.Values)
                if (plugin.GetDrawOrder() == PluginDrawOrder.First)
                    plugin.OnDraw();
            if (back != null)
                Engine.device.VideoDriver.Draw2DImage(back, new Vector2Di(0, 0));
            foreach (ICustomPlugin plugin in plugins.Values)
                if (plugin.GetDrawOrder() == PluginDrawOrder.AfterBackground)
                    plugin.OnDraw();
            Engine.device.SceneManager.DrawAll();
            foreach (ICustomPlugin plugin in plugins.Values)
                if (plugin.GetDrawOrder() == PluginDrawOrder.AfterObjects)
                    plugin.OnDraw();
            if (foreground != null)
                Engine.device.VideoDriver.Draw2DImage(foreground, new Vector2Di());
            foreach (Recti rect in rects.Values)
                rect.DrawNinePatch();
            foreach (Menu menu in menus.Values)
                menu.Draw();
            foreach (UISprite sprite in sprites.Values)
                sprite.Draw();
            foreach (UIText text in texts.Values)
                text.Draw();
            if (pointer != null)
                pointer.Draw();
            foreach (ICustomPlugin plugin in plugins.Values)
                if (plugin.GetDrawOrder() == PluginDrawOrder.AfterUI)
                    plugin.OnDraw();
            Engine.device.VideoDriver.Draw2DRectangle(Engine.device.VideoDriver.ViewPort, faderColor);
            foreach (ICustomPlugin plugin in plugins.Values)
                if (plugin.GetDrawOrder() == PluginDrawOrder.AfterFader)
                    plugin.OnDraw();
            Engine.device.VideoDriver.EndScene();
        }
    }
}
